/*-------------------------------------------------------------------------
 *
 * regc_pg_locale.c
 *	  适应于 pg_wchar（即 chr）的字符类型函数，
 *	  以及缓存批量字符类型探测结果的函数。
 *
 * 此文件由 regcomp.c 包含；它不是独立编译的。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/regex/regc_pg_locale.c
 *
 *-------------------------------------------------------------------------
 */

#include "catalog/pg_collation.h"
#include "utils/pg_locale.h"

/*
 * 为了在各种平台上提供尽可能多的功能，
 * 而不必从头实现所有内容，我们根据情况使用
 * 几种实施策略：
 *
 * 1. 在C/POSIX比较中，我们使用硬编码。我们不能依赖
 * <ctype.h>函数，因为这些将遵从LC_CTYPE。请注意，这些
 * 比较对多字节字符毫不在意。
 *
 * 2. 在“默认”比较（应该遵从LC_CTYPE）中：
 *
 * 2a. 在使用UTF8编码时，我们使用<wctype.h>函数。
 * 这假设每个平台直接使用Unicode代码点
 * 作为Unicode的wchar_t表示形式。在某些平台上
 * wchar_t仅为16位宽，因此对于codepoint > 0xFFFF我们必须放弃处理。
 *
 * 2b. 在所有其他编码中，对于pg_wchar
 * 值（高达255），我们使用<ctype.h>函数，对于超出该范围的值则放弃处理。
 * 这在例如LATINn的单字节编码中是100%正确的。但非Unicode
 * 的多字节编码主要是远东字符集，因此在这里测试的属性
 * 对于较高的代码值不是很相关。使用<wctype.h>函数与
 * 非Unicode多字节编码的困难在于，我们无法确保
 * 平台的wchar_t表示与我们在pg_wchar
 * 转换中所做的匹配。
 *
 * 3. 其他排序只在具有HAVE_LOCALE_T的平台上受支持。
 * 在这里，我们在与#2完全相同的情况下，使用< wctype.h>和<ctype.h>
 * 函数的locale_t扩展形式。
 *
 * 情况2和3之间有一个显著区别：在“默认”
 * 比较中，我们强制ASCII字母遵循ASCII大写/小写规则，
 * 而在非默认比较中，我们只让库函数随其所愿。需要注意的情况是
 * 土耳其语中I/i的处理，其行为意在匹配
 * upper()/lower() SQL函数。
 *
 * 我们将活动比较设置存储在静态变量中。原则上
 * 它可以通过正则表达式库的“struct vars”数据
 * 结构传递到这里；但这将需要对正则表达式
 * 库进行一些侵入性的更改，目前并没有实际的益处。
 *
 * 注意：这里的编码假设pg_wchar是无符号类型。
 */

typedef enum
{
	PG_REGEX_LOCALE_C,			/* C区域设置（编码无关） */
	PG_REGEX_LOCALE_WIDE,		/* 使用<wctype.h>函数 */
	PG_REGEX_LOCALE_1BYTE,		/* 使用<ctype.h>函数 */
	PG_REGEX_LOCALE_WIDE_L,		/* 使用locale_t <wctype.h>函数 */
	PG_REGEX_LOCALE_1BYTE_L,	/* 使用locale_t <ctype.h>函数 */
	PG_REGEX_LOCALE_ICU			/* 使用ICU uchar.h函数 */
} PG_Locale_Strategy;

static PG_Locale_Strategy pg_regex_strategy;
static pg_locale_t pg_regex_locale;
static Oid	pg_regex_collation;

/*
 * C区域设置的硬编码字符属性
 */
#define PG_ISDIGIT	0x01
#define PG_ISALPHA	0x02
#define PG_ISALNUM	(PG_ISDIGIT | PG_ISALPHA)
#define PG_ISUPPER	0x04
#define PG_ISLOWER	0x08
#define PG_ISGRAPH	0x10
#define PG_ISPRINT	0x20
#define PG_ISPUNCT	0x40
#define PG_ISSPACE	0x80

static const unsigned char pg_char_properties[128] = {
	 /* NUL */ 0,
	 /* ^A */ 0,
	 /* ^B */ 0,
	 /* ^C */ 0,
	 /* ^D */ 0,
	 /* ^E */ 0,
	 /* ^F */ 0,
	 /* ^G */ 0,
	 /* ^H */ 0,
	 /* ^I */ PG_ISSPACE,
	 /* ^J */ PG_ISSPACE,
	 /* ^K */ PG_ISSPACE,
	 /* ^L */ PG_ISSPACE,
	 /* ^M */ PG_ISSPACE,
	 /* ^N */ 0,
	 /* ^O */ 0,
	 /* ^P */ 0,
	 /* ^Q */ 0,
	 /* ^R */ 0,
	 /* ^S */ 0,
	 /* ^T */ 0,
	 /* ^U */ 0,
	 /* ^V */ 0,
	 /* ^W */ 0,
	 /* ^X */ 0,
	 /* ^Y */ 0,
	 /* ^Z */ 0,
	 /* ^[ */ 0,
	 /* ^\ */ 0,
	 /* ^] */ 0,
	 /* ^^ */ 0,
	 /* ^_ */ 0,
	 /* */ PG_ISPRINT | PG_ISSPACE,
	 /* !  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* "  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* #  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* $  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* %  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* &  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* '  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* (  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* )  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* *  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* +  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* ,  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* -  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* .  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* /  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* 0  */ PG_ISDIGIT | PG_ISGRAPH | PG_ISPRINT,
	 /* 1  */ PG_ISDIGIT | PG_ISGRAPH | PG_ISPRINT,
	 /* 2  */ PG_ISDIGIT | PG_ISGRAPH | PG_ISPRINT,
	 /* 3  */ PG_ISDIGIT | PG_ISGRAPH | PG_ISPRINT,
	 /* 4  */ PG_ISDIGIT | PG_ISGRAPH | PG_ISPRINT,
	 /* 5  */ PG_ISDIGIT | PG_ISGRAPH | PG_ISPRINT,
	 /* 6  */ PG_ISDIGIT | PG_ISGRAPH | PG_ISPRINT,
	 /* 7  */ PG_ISDIGIT | PG_ISGRAPH | PG_ISPRINT,
	 /* 8  */ PG_ISDIGIT | PG_ISGRAPH | PG_ISPRINT,
	 /* 9  */ PG_ISDIGIT | PG_ISGRAPH | PG_ISPRINT,
	 /* :  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* ;  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* <  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* =  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* >  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* ?  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* @  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* A  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* B  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* C  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* D  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* E  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* F  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* G  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* H  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* I  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* J  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* K  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* L  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* M  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* N  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* O  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* P  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* Q  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* R  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* S  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* T  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* U  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* V  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* W  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* X  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* Y  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* Z  */ PG_ISALPHA | PG_ISUPPER | PG_ISGRAPH | PG_ISPRINT,
	 /* [  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* \  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* ]  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* ^  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* _  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* `  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* a  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* b  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* c  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* d  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* e  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* f  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* g  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* h  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* i  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* j  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* k  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* l  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* m  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* n  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* o  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* p  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* q  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* r  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* s  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* t  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* u  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* v  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* w  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* x  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* y  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* z  */ PG_ISALPHA | PG_ISLOWER | PG_ISGRAPH | PG_ISPRINT,
	 /* {  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* |  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* }  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* ~  */ PG_ISGRAPH | PG_ISPRINT | PG_ISPUNCT,
	 /* DEL */ 0
};


/*
 * pg_set_regex_collation: 设置这些函数的比较以遵循
 *
 * 在开始编译或执行正则表达式时调用。
 * 由于不需要正则表达式操作的再入性，存储结果在静态变量中是可以的。
 */
void pg_set_regex_collation(Oid fc_collation)
{
	if (!OidIsValid(fc_collation))
	{
		/*
		 * 这通常意味着解析器无法解决隐式排序的冲突，
		 * 因此以这种方式报告。
		 */
		ereport(ERROR,
				(errcode(ERRCODE_INDETERMINATE_COLLATION),
				 errmsg("could not determine which collation to use for regular expression"),
				 errhint("Use the COLLATE clause to set the collation explicitly.")));
	}

	if (lc_ctype_is_c(fc_collation))
	{
		/* C/POSIX比较无论数据库编码如何都使用此路径 */
		pg_regex_strategy = PG_REGEX_LOCALE_C;
		pg_regex_locale = 0;
		pg_regex_collation = C_COLLATION_OID;
	}
	else
	{
		/*
		 * 注意：如果没有HAVE_LOCALE_T，pg_newlocale_from_collation将失败；
		 * pg_regex_locale != 0但没有HAVE_LOCALE_T的情况不必
		 * 在下面考虑。
		 */
		pg_regex_locale = pg_newlocale_from_collation(fc_collation);

		if (pg_regex_locale && !pg_regex_locale->deterministic)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("nondeterministic collations are not supported for regular expressions")));

#ifdef USE_ICU
		if (pg_regex_locale && pg_regex_locale->provider == COLLPROVIDER_ICU)
			pg_regex_strategy = PG_REGEX_LOCALE_ICU;
		else
#endif
		if (GetDatabaseEncoding() == PG_UTF8)
		{
			if (pg_regex_locale)
				pg_regex_strategy = PG_REGEX_LOCALE_WIDE_L;
			else
				pg_regex_strategy = PG_REGEX_LOCALE_WIDE;
		}
		else
		{
			if (pg_regex_locale)
				pg_regex_strategy = PG_REGEX_LOCALE_1BYTE_L;
			else
				pg_regex_strategy = PG_REGEX_LOCALE_1BYTE;
		}

		pg_regex_collation = fc_collation;
	}
}

static int pg_wc_isdigit(pg_wchar fc_c)
{
	switch (pg_regex_strategy)
	{
		case PG_REGEX_LOCALE_C:
			return (fc_c <= (pg_wchar) 127 &&
					(pg_char_properties[fc_c] & PG_ISDIGIT));
		case PG_REGEX_LOCALE_WIDE:
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswdigit((wint_t) fc_c);
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE:
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					isdigit((unsigned char) fc_c));
		case PG_REGEX_LOCALE_WIDE_L:
#ifdef HAVE_LOCALE_T
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswdigit_l((wint_t) fc_c, pg_regex_locale->info.lt);
#endif
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE_L:
#ifdef HAVE_LOCALE_T
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					isdigit_l((unsigned char) fc_c, pg_regex_locale->info.lt));
#endif
			break;
		case PG_REGEX_LOCALE_ICU:
#ifdef USE_ICU
			return u_isdigit(fc_c);
#endif
			break;
	}
	return 0;					/* 不能到这里，但保持编译器安静 */
}

static int pg_wc_isalpha(pg_wchar fc_c)
{
	switch (pg_regex_strategy)
	{
		case PG_REGEX_LOCALE_C:
			return (fc_c <= (pg_wchar) 127 &&
					(pg_char_properties[fc_c] & PG_ISALPHA));
		case PG_REGEX_LOCALE_WIDE:
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswalpha((wint_t) fc_c);
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE:
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					isalpha((unsigned char) fc_c));
		case PG_REGEX_LOCALE_WIDE_L:
#ifdef HAVE_LOCALE_T
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswalpha_l((wint_t) fc_c, pg_regex_locale->info.lt);
#endif
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE_L:
#ifdef HAVE_LOCALE_T
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					isalpha_l((unsigned char) fc_c, pg_regex_locale->info.lt));
#endif
			break;
		case PG_REGEX_LOCALE_ICU:
#ifdef USE_ICU
			return u_isalpha(fc_c);
#endif
			break;
	}
	return 0;					/* 不能到这里，但保持编译器安静 */
}

static int pg_wc_isalnum(pg_wchar fc_c)
{
	switch (pg_regex_strategy)
	{
		case PG_REGEX_LOCALE_C:
			return (fc_c <= (pg_wchar) 127 &&
					(pg_char_properties[fc_c] & PG_ISALNUM));
		case PG_REGEX_LOCALE_WIDE:
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswalnum((wint_t) fc_c);
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE:
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					isalnum((unsigned char) fc_c));
		case PG_REGEX_LOCALE_WIDE_L:
#ifdef HAVE_LOCALE_T
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswalnum_l((wint_t) fc_c, pg_regex_locale->info.lt);
#endif
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE_L:
#ifdef HAVE_LOCALE_T
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					isalnum_l((unsigned char) fc_c, pg_regex_locale->info.lt));
#endif
			break;
		case PG_REGEX_LOCALE_ICU:
#ifdef USE_ICU
			return u_isalnum(fc_c);
#endif
			break;
	}
	return 0;					/* 不能到这里，但保持编译器安静 */
}

static int pg_wc_isword(pg_wchar fc_c)
{
	/* 我们将单词字符定义为字母数字类加下划线 */
	if (fc_c == CHR('_'))
		return 1;
	return pg_wc_isalnum(fc_c);
}

static int pg_wc_isupper(pg_wchar fc_c)
{
	switch (pg_regex_strategy)
	{
		case PG_REGEX_LOCALE_C:
			return (fc_c <= (pg_wchar) 127 &&
					(pg_char_properties[fc_c] & PG_ISUPPER));
		case PG_REGEX_LOCALE_WIDE:
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswupper((wint_t) fc_c);
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE:
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					isupper((unsigned char) fc_c));
		case PG_REGEX_LOCALE_WIDE_L:
#ifdef HAVE_LOCALE_T
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswupper_l((wint_t) fc_c, pg_regex_locale->info.lt);
#endif
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE_L:
#ifdef HAVE_LOCALE_T
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					isupper_l((unsigned char) fc_c, pg_regex_locale->info.lt));
#endif
			break;
		case PG_REGEX_LOCALE_ICU:
#ifdef USE_ICU
			return u_isupper(fc_c);
#endif
			break;
	}
	return 0;					/* 不能到这里，但保持编译器安静 */
}

static int pg_wc_islower(pg_wchar fc_c)
{
	switch (pg_regex_strategy)
	{
		case PG_REGEX_LOCALE_C:
			return (fc_c <= (pg_wchar) 127 &&
					(pg_char_properties[fc_c] & PG_ISLOWER));
		case PG_REGEX_LOCALE_WIDE:
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswlower((wint_t) fc_c);
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE:
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					islower((unsigned char) fc_c));
		case PG_REGEX_LOCALE_WIDE_L:
#ifdef HAVE_LOCALE_T
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswlower_l((wint_t) fc_c, pg_regex_locale->info.lt);
#endif
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE_L:
#ifdef HAVE_LOCALE_T
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					islower_l((unsigned char) fc_c, pg_regex_locale->info.lt));
#endif
			break;
		case PG_REGEX_LOCALE_ICU:
#ifdef USE_ICU
			return u_islower(fc_c);
#endif
			break;
	}
	return 0;					/* 不能到这里，但保持编译器安静 */
}

static int pg_wc_isgraph(pg_wchar fc_c)
{
	switch (pg_regex_strategy)
	{
		case PG_REGEX_LOCALE_C:
			return (fc_c <= (pg_wchar) 127 &&
					(pg_char_properties[fc_c] & PG_ISGRAPH));
		case PG_REGEX_LOCALE_WIDE:
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswgraph((wint_t) fc_c);
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE:
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					isgraph((unsigned char) fc_c));
		case PG_REGEX_LOCALE_WIDE_L:
#ifdef HAVE_LOCALE_T
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswgraph_l((wint_t) fc_c, pg_regex_locale->info.lt);
#endif
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE_L:
#ifdef HAVE_LOCALE_T
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					isgraph_l((unsigned char) fc_c, pg_regex_locale->info.lt));
#endif
			break;
		case PG_REGEX_LOCALE_ICU:
#ifdef USE_ICU
			return u_isgraph(fc_c);
#endif
			break;
	}
	return 0;					/* 不能到这里，但保持编译器安静 */
}

static int pg_wc_isprint(pg_wchar fc_c)
{
	switch (pg_regex_strategy)
	{
		case PG_REGEX_LOCALE_C:
			return (fc_c <= (pg_wchar) 127 &&
					(pg_char_properties[fc_c] & PG_ISPRINT));
		case PG_REGEX_LOCALE_WIDE:
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswprint((wint_t) fc_c);
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE:
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					isprint((unsigned char) fc_c));
		case PG_REGEX_LOCALE_WIDE_L:
#ifdef HAVE_LOCALE_T
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswprint_l((wint_t) fc_c, pg_regex_locale->info.lt);
#endif
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE_L:
#ifdef HAVE_LOCALE_T
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					isprint_l((unsigned char) fc_c, pg_regex_locale->info.lt));
#endif
			break;
		case PG_REGEX_LOCALE_ICU:
#ifdef USE_ICU
			return u_isprint(fc_c);
#endif
			break;
	}
	return 0;					/* 不能到这里，但保持编译器安静 */
}

static int pg_wc_ispunct(pg_wchar fc_c)
{
	switch (pg_regex_strategy)
	{
		case PG_REGEX_LOCALE_C:
			return (fc_c <= (pg_wchar) 127 &&
					(pg_char_properties[fc_c] & PG_ISPUNCT));
		case PG_REGEX_LOCALE_WIDE:
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswpunct((wint_t) fc_c);
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE:
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					ispunct((unsigned char) fc_c));
		case PG_REGEX_LOCALE_WIDE_L:
#ifdef HAVE_LOCALE_T
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswpunct_l((wint_t) fc_c, pg_regex_locale->info.lt);
#endif
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE_L:
#ifdef HAVE_LOCALE_T
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					ispunct_l((unsigned char) fc_c, pg_regex_locale->info.lt));
#endif
			break;
		case PG_REGEX_LOCALE_ICU:
#ifdef USE_ICU
			return u_ispunct(fc_c);
#endif
			break;
	}
	return 0;					/* 不能到这里，但保持编译器安静 */
}

static int pg_wc_isspace(pg_wchar fc_c)
{
	switch (pg_regex_strategy)
	{
		case PG_REGEX_LOCALE_C:
			return (fc_c <= (pg_wchar) 127 &&
					(pg_char_properties[fc_c] & PG_ISSPACE));
		case PG_REGEX_LOCALE_WIDE:
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswspace((wint_t) fc_c);
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE:
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					isspace((unsigned char) fc_c));
		case PG_REGEX_LOCALE_WIDE_L:
#ifdef HAVE_LOCALE_T
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return iswspace_l((wint_t) fc_c, pg_regex_locale->info.lt);
#endif
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE_L:
#ifdef HAVE_LOCALE_T
			return (fc_c <= (pg_wchar) UCHAR_MAX &&
					isspace_l((unsigned char) fc_c, pg_regex_locale->info.lt));
#endif
			break;
		case PG_REGEX_LOCALE_ICU:
#ifdef USE_ICU
			return u_isspace(fc_c);
#endif
			break;
	}
	return 0;					/* 不能到这里，但保持编译器安静 */
}

static pg_wchar pg_wc_toupper(pg_wchar fc_c)
{
	switch (pg_regex_strategy)
	{
		case PG_REGEX_LOCALE_C:
			if (fc_c <= (pg_wchar) 127)
				return pg_ascii_toupper((unsigned char) fc_c);
			return fc_c;
		case PG_REGEX_LOCALE_WIDE:
			/* 强制C对ASCII字符的行为，遵循上述评论 */
			if (fc_c <= (pg_wchar) 127)
				return pg_ascii_toupper((unsigned char) fc_c);
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return towupper((wint_t) fc_c);
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE:
			/* 强制C对ASCII字符的行为，遵循上述评论 */
			if (fc_c <= (pg_wchar) 127)
				return pg_ascii_toupper((unsigned char) fc_c);
			if (fc_c <= (pg_wchar) UCHAR_MAX)
				return toupper((unsigned char) fc_c);
			return fc_c;
		case PG_REGEX_LOCALE_WIDE_L:
#ifdef HAVE_LOCALE_T
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return towupper_l((wint_t) fc_c, pg_regex_locale->info.lt);
#endif
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE_L:
#ifdef HAVE_LOCALE_T
			if (fc_c <= (pg_wchar) UCHAR_MAX)
				return toupper_l((unsigned char) fc_c, pg_regex_locale->info.lt);
#endif
			return fc_c;
		case PG_REGEX_LOCALE_ICU:
#ifdef USE_ICU
			return u_toupper(fc_c);
#endif
			break;
	}
	return 0;					/* 不能到这里，但保持编译器安静 */
}

static pg_wchar pg_wc_tolower(pg_wchar fc_c)
{
	switch (pg_regex_strategy)
	{
		case PG_REGEX_LOCALE_C:
			if (fc_c <= (pg_wchar) 127)
				return pg_ascii_tolower((unsigned char) fc_c);
			return fc_c;
		case PG_REGEX_LOCALE_WIDE:
			/* 强制C对ASCII字符的行为，遵循上述评论 */
			if (fc_c <= (pg_wchar) 127)
				return pg_ascii_tolower((unsigned char) fc_c);
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return towlower((wint_t) fc_c);
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE:
			/* 强制C对ASCII字符的行为，遵循上述评论 */
			if (fc_c <= (pg_wchar) 127)
				return pg_ascii_tolower((unsigned char) fc_c);
			if (fc_c <= (pg_wchar) UCHAR_MAX)
				return tolower((unsigned char) fc_c);
			return fc_c;
		case PG_REGEX_LOCALE_WIDE_L:
#ifdef HAVE_LOCALE_T
			if (sizeof(wchar_t) >= 4 || fc_c <= (pg_wchar) 0xFFFF)
				return towlower_l((wint_t) fc_c, pg_regex_locale->info.lt);
#endif
			/* FALL THRU */
		case PG_REGEX_LOCALE_1BYTE_L:
#ifdef HAVE_LOCALE_T
			if (fc_c <= (pg_wchar) UCHAR_MAX)
				return tolower_l((unsigned char) fc_c, pg_regex_locale->info.lt);
#endif
			return fc_c;
		case PG_REGEX_LOCALE_ICU:
#ifdef USE_ICU
			return u_tolower(fc_c);
#endif
			break;
	}
	return 0;					/* 不能到这里，但保持编译器安静 */
}


/*
 * 这些函数缓存了在给定编码/比较中对
 * 所有感兴趣字符代码进行libc的ctype行为的探测结果。
 * 结果作为“struct cvec”提供，但请注意，表示方式
 * 与由regc_cvec.c创建的cvec略有不同：我们单独分配
 * chrs[]和ranges[]数组，以便根据需要进行更大
 * 的realloc。这是可以的，因为在这里制作的cvec
 * 永远不应被freecvec()释放。
 *
 * 我们使用malloc而不是palloc，因为我们不能在内存耗尽的情况下失去控制；
 * 主要的正则表达式代码期望我们返回失败指示。
 */

typedef int (*pg_wc_probefunc) (pg_wchar fc_c);

typedef struct pg_ctype_cache
{
	pg_wc_probefunc probefunc;	/* pg_wc_isalpha或其兄弟 */
	Oid			collation;		/* 此条目的比较 */
	struct cvec cv;				/* 缓存条目内容 */
	struct pg_ctype_cache *next;	
/* 链接 */
} pg_ctype_cache;

static pg_ctype_cache *pg_ctype_cache_list = NULL;

/*
 * 向 pcc->cv 添加一个字符或范围；如果内存耗尽则返回 false
 */
static bool store_match(pg_ctype_cache *fc_pcc, pg_wchar fc_chr1, int fc_nchrs)
{
	chr		   *fc_newchrs;

	if (fc_nchrs > 1)
	{
		if (fc_pcc->cv.nranges >= fc_pcc->cv.rangespace)
		{
			fc_pcc->cv.rangespace *= 2;
			fc_newchrs = (chr *) realloc(fc_pcc->cv.ranges,
									  fc_pcc->cv.rangespace * sizeof(chr) * 2);
			if (fc_newchrs == NULL)
				return false;
			fc_pcc->cv.ranges = fc_newchrs;
		}
		fc_pcc->cv.ranges[fc_pcc->cv.nranges * 2] = fc_chr1;
		fc_pcc->cv.ranges[fc_pcc->cv.nranges * 2 + 1] = fc_chr1 + fc_nchrs - 1;
		fc_pcc->cv.nranges++;
	}
	else
	{
		assert(fc_nchrs == 1);
		if (fc_pcc->cv.nchrs >= fc_pcc->cv.chrspace)
		{
			fc_pcc->cv.chrspace *= 2;
			fc_newchrs = (chr *) realloc(fc_pcc->cv.chrs,
									  fc_pcc->cv.chrspace * sizeof(chr));
			if (fc_newchrs == NULL)
				return false;
			fc_pcc->cv.chrs = fc_newchrs;
		}
		fc_pcc->cv.chrs[fc_pcc->cv.nchrs++] = fc_chr1;
	}
	return true;
}

/*
 * 给定一个探测函数（例如：pg_wc_isalpha），为所有满足探测函数的字符获取一个 struct cvec。
 * 当前的排序是之前通过 pg_set_regex_collation 设置的。如果内存耗尽则返回 NULL。
 *
 * 请注意，结果不能被调用者释放或修改。
 */
static struct cvec * pg_ctype_get_cache(pg_wc_probefunc fc_probefunc, int fc_cclasscode)
{
	pg_ctype_cache *fc_pcc;
	pg_wchar	fc_max_chr;
	pg_wchar	fc_cur_chr;
	int			fc_nmatches;
	chr		   *fc_newchrs;

	/*
	 * 我们已经缓存了答案吗？
	 */
	for (fc_pcc = pg_ctype_cache_list; fc_pcc != NULL; fc_pcc = fc_pcc->next)
	{
		if (fc_pcc->probefunc == fc_probefunc &&
			fc_pcc->collation == pg_regex_collation)
			return &fc_pcc->cv;
	}

	/*
	 * 不，初始化一些工作区域...
	 */
	fc_pcc = (pg_ctype_cache *) malloc(sizeof(pg_ctype_cache));
	if (fc_pcc == NULL)
		return NULL;
	fc_pcc->probefunc = fc_probefunc;
	fc_pcc->collation = pg_regex_collation;
	fc_pcc->cv.nchrs = 0;
	fc_pcc->cv.chrspace = 128;
	fc_pcc->cv.chrs = (chr *) malloc(fc_pcc->cv.chrspace * sizeof(chr));
	fc_pcc->cv.nranges = 0;
	fc_pcc->cv.rangespace = 64;
	fc_pcc->cv.ranges = (chr *) malloc(fc_pcc->cv.rangespace * sizeof(chr) * 2);
	if (fc_pcc->cv.chrs == NULL || fc_pcc->cv.ranges == NULL)
		goto out_of_memory;
	fc_pcc->cv.cclasscode = fc_cclasscode;

	/*
	 * 决定我们应该查看多少个字符编码。通常情况下，我们不会超过 MAX_SIMPLE_CHR；超出该值的字符编码在运行时使用 “高色彩图” 机制处理。然而，在 C 区域中，最多只需查看到 127，如果我们只有一个 1 字节的 <ctype.h> API，则没有必要查看超过它能够处理的范围。
	 *
	 * 如果不是 MAX_SIMPLE_CHR 限制了搜索，则将输出 cvec 标记为没有任何区域相关的行为，因为不需要进行任何运行时区域检查。（这里的 #if 在生产值为 MAX_SIMPLE_CHR 时总是为真，但允许其在测试中小一些是有用的。）
	 */
	switch (pg_regex_strategy)
	{
		case PG_REGEX_LOCALE_C:
#if MAX_SIMPLE_CHR >= 127
			fc_max_chr = (pg_wchar) 127;
			fc_pcc->cv.cclasscode = -1;
#else
			fc_max_chr = (pg_wchar) MAX_SIMPLE_CHR;
#endif
			break;
		case PG_REGEX_LOCALE_WIDE:
		case PG_REGEX_LOCALE_WIDE_L:
			fc_max_chr = (pg_wchar) MAX_SIMPLE_CHR;
			break;
		case PG_REGEX_LOCALE_1BYTE:
		case PG_REGEX_LOCALE_1BYTE_L:
#if MAX_SIMPLE_CHR >= UCHAR_MAX
			fc_max_chr = (pg_wchar) UCHAR_MAX;
			fc_pcc->cv.cclasscode = -1;
#else
			fc_max_chr = (pg_wchar) MAX_SIMPLE_CHR;
#endif
			break;
		case PG_REGEX_LOCALE_ICU:
			fc_max_chr = (pg_wchar) MAX_SIMPLE_CHR;
			break;
		default:
			fc_max_chr = 0;		/* 不能到这里，但保持编译器安静 */
			break;
	}

	/*
	 * 然后扫描它们...
	 */
	fc_nmatches = 0;				/* 连续匹配的数量 */

	for (fc_cur_chr = 0; fc_cur_chr <= fc_max_chr; fc_cur_chr++)
	{
		if ((*fc_probefunc) (fc_cur_chr))
			fc_nmatches++;
		else if (fc_nmatches > 0)
		{
			if (!store_match(fc_pcc, fc_cur_chr - fc_nmatches, fc_nmatches))
				goto out_of_memory;
			fc_nmatches = 0;
		}
	}

	if (fc_nmatches > 0)
		if (!store_match(fc_pcc, fc_cur_chr - fc_nmatches, fc_nmatches))
			goto out_of_memory;

	/*
	 * 如果分配的内存超过所需，则释放它
	 */
	if (fc_pcc->cv.nchrs == 0)
	{
		free(fc_pcc->cv.chrs);
		fc_pcc->cv.chrs = NULL;
		fc_pcc->cv.chrspace = 0;
	}
	else if (fc_pcc->cv.nchrs < fc_pcc->cv.chrspace)
	{
		fc_newchrs = (chr *) realloc(fc_pcc->cv.chrs,
								  fc_pcc->cv.nchrs * sizeof(chr));
		if (fc_newchrs == NULL)
			goto out_of_memory;
		fc_pcc->cv.chrs = fc_newchrs;
		fc_pcc->cv.chrspace = fc_pcc->cv.nchrs;
	}
	if (fc_pcc->cv.nranges == 0)
	{
		free(fc_pcc->cv.ranges);
		fc_pcc->cv.ranges = NULL;
		fc_pcc->cv.rangespace = 0;
	}
	else if (fc_pcc->cv.nranges < fc_pcc->cv.rangespace)
	{
		fc_newchrs = (chr *) realloc(fc_pcc->cv.ranges,
								  fc_pcc->cv.nranges * sizeof(chr) * 2);
		if (fc_newchrs == NULL)
			goto out_of_memory;
		fc_pcc->cv.ranges = fc_newchrs;
		fc_pcc->cv.rangespace = fc_pcc->cv.nranges;
	}

	/*
	 * 成功，将其链接到缓存链中
	 */
	fc_pcc->next = pg_ctype_cache_list;
	pg_ctype_cache_list = fc_pcc;

	return &fc_pcc->cv;

	/*
	 * 失败，进行清理
	 */
out_of_memory:
	if (fc_pcc->cv.chrs)
		free(fc_pcc->cv.chrs);
	if (fc_pcc->cv.ranges)
		free(fc_pcc->cv.ranges);
	free(fc_pcc);

	return NULL;
}
